#include "modulemanager.h"
#include "tools/m_sleep.h"
#include "config.h"
#include "physicallinkmanager.h"
#include "logger.h"

ModuleManager*   ModuleManager::instanceM = nullptr;
MMutex           ModuleManager::instanceLockM;

/*
** 函数名称: getInstance()
** 功能描述: 单例
** 参    数: 无
** 返 回 值: 全局指针
** 备    注:
*/
ModuleManager* ModuleManager::getInstance()
{
    if (instanceM == nullptr)
    {
        AUTO_GUARD(gd, MMutex, instanceLockM);
        if (instanceM == nullptr)
        {
            instanceM = new ModuleManager();
        }
    }
    return instanceM;
}

/*
** 函数名称: ModuleManager()
** 功能描述: 构造函数
** 参    数: 无
** 返 回 值: 无
** 备    注:
*/
ModuleManager::ModuleManager()
:   initCntM(10)
,   checkedFilePathM(MODULE_FILE)
,   mapFilePathM(COM_REMAP_FILE)
,   checkTimeM(5 * 1000)
,   nextCheckTimeM(0)
{
    init();
}

/*
** 函数名称: ~ModuleManager()
** 功能描述: 析构函数
** 参    数: 无
** 返 回 值: 无
** 备    注:
*/
ModuleManager::~ModuleManager()
{

}

/*
** 函数名称: ~init()
** 功能描述: 初始化模组管理器内部参数
** 参    数: 无
** 返 回 值: 无
** 备    注:
*/
void ModuleManager::init()
{
    mapM[1<<8|1] = std::string("SLOT1_RS485-1");
    mapM[1<<8|2] = std::string("SLOT1_RS485-2");
    mapM[1<<8|3] = std::string("SLOT1_RS485-3");
    mapM[1<<8|4] = std::string("SLOT1_RS485-4");
    mapM[2<<8|1] = std::string("SLOT2_RS485-1");
    mapM[2<<8|2] = std::string("SLOT2_RS485-2");
    mapM[2<<8|3] = std::string("SLOT2_RS485-3");
    mapM[2<<8|4] = std::string("SLOT2_RS485-4");
    mapM[3<<8|1] = std::string("SLOT3_RS485-1");
    mapM[3<<8|2] = std::string("SLOT3_RS485-2");
    mapM[3<<8|3] = std::string("SLOT3_RS485-3");
    mapM[3<<8|4] = std::string("SLOT3_RS485-4");
    mapM[4<<8|1] = std::string("SLOT4_RS485-1");
    mapM[4<<8|2] = std::string("SLOT4_RS485-2");
    mapM[4<<8|3] = std::string("SLOT4_RS485-3");
    mapM[4<<8|4] = std::string("SLOT4_RS485-4");
    mapM[5<<8|1] = std::string("SLOT5_RS485-1");
    mapM[5<<8|2] = std::string("SLOT5_RS485-2");
    mapM[5<<8|3] = std::string("SLOT5_RS485-3");
    mapM[5<<8|4] = std::string("SLOT5_RS485-4");

    nextCheckTimeM = 0;
    memset(&mTimeM, 0, sizeof(mTimeM));

    std::map<std::string, std::string> m = PhysicalLinkManager::mgr()->getRemapCom();
    for (auto it = mapM.begin(); it != mapM.end(); ++it)
    {
        if (m.find(it->second) != m.end())
        {
            remapM[it->second] = m[it->second];
        }
        else
        {
            remapM[it->second] = "";
        }
    }

}

/*
** 函数名称: check()
** 功能描述: 检查一次模组文件,并更新串口映射文件comReamp
** 参    数: 无
** 返 回 值: bool               有无更新
** 备    注:
*/
bool ModuleManager::check()
{
    struct stat tmpStat;
    if (stat(checkedFilePathM.c_str(), &tmpStat) != 0)
    {
        log_error("/tmp/module.json stat failed.");
        return false;
    }
    else{}

    //最后修改文件时间是否发生变化
    if ((mTimeM.tv_sec == tmpStat.st_mtim.tv_sec) &&
        (mTimeM.tv_nsec == tmpStat.st_mtim.tv_nsec))
    {
        return false;
    }

    mTimeM.tv_sec = tmpStat.st_mtim.tv_sec;
    mTimeM.tv_nsec = tmpStat.st_mtim.tv_nsec;

    //获取最新的模组插入信息
    std::map<std::string, std::string> m;
    std::string file(checkedFilePathM);
    rapidjson::Document doc;
    if (rapidjson::parseFile(file.c_str(), doc))
    {
        for (int i = MIN_OF_SLOT_ID; i <= MAX_OF_SLOT_ID; ++i)
        {
            parseOne(doc, i, m);
        }
    }else{}

    bool isModify(false);
    //匹配模组信息与映射文件
    for (auto it = remapM.begin(); it != remapM.end(); ++it)
    {
        if (m.find(it->first) != m.end() &&
            m[it->first] != it->second)
        {
            it->second = m[it->first];
            isModify = true;
        }
    }

    if (isModify)
    {
        updateComs();
        return true;
    }

    return false;
}

/*
** 函数名称: parseOne()
** 功能描述: 解析模组文件单个模组的信息
** 参    数: doc                模组文件的json 文档根
**           theSlotId          模组ID
**           m                  映射信息保存结构
** 返 回 值: 无
** 备    注:
*/
void ModuleManager::parseOne(rapidjson::Document &doc, int theSlotId, std::map<std::string, std::string>& m)
{
    rapidjson::Value* module;
    if (theSlotId >= MIN_OF_SLOT_ID && theSlotId <=MAX_OF_SLOT_ID)
    {
        module  = rapidjson::getChild(doc, std::to_string(theSlotId).c_str());
    }

    if (module && module->IsObject())
    {
        rapidjson::Value* info = rapidjson::getChild(module, "info");
        if (info && info->IsObject())
        {
            //判断是否已经插入模组，如果未插入则不需要进一步检测
            bool isPluged = rapidjson::getInt(info, "ispluged", false);
            if (isPluged)
            {
                rapidjson::Value* ports = rapidjson::getChild(module, "ports");
                if (ports && ports->IsArray())
                {
                    for (auto port = ports->Begin();
                        port != ports->End(); ++port)
                    {
                        int id(rapidjson::getInt(port, "id", 0));
                        int type(rapidjson::getInt(port, "type", 0));
                        std::string path(rapidjson::getString(port, "path", ""));

                        if (id >= MIN_OF_UART_ID && id <= MAX_OF_UART_ID && type == 3)
                        {
                            m[mapM[theSlotId << 8 | id]] = path;
                        }
                    }
                    return;
                }
            }
            else{}
        }
    }
    else{}
unpluged:
    for (int id = MIN_OF_UART_ID; id <= MAX_OF_UART_ID; ++id)
    {
        m[mapM[theSlotId << 8 | id]] = mapM[theSlotId << 8 | id];
    }

}

/*
** 函数名称: updateComs()
** 功能描述: 将串口映射信息更新写入串口管理
** 参    数: 无
** 返 回 值: 无
** 备    注:
*/
void ModuleManager::updateComs()
{
    std::map<std::string, std::string>& m = PhysicalLinkManager::mgr()->getRemapCom();
    for (auto it = m.begin(); it != m.end(); ++it)
    {
        if (remapM.find(it->first) != remapM.end())
        {
            m[it->first] =  remapM[it->first];
        }
    }
}

/*
** 函数名称: saveRemap()
** 功能描述: 将串口映射信息更新写入串口映射文件comReamp
** 参    数: 无
** 返 回 值: 无
** 备    注:
*/
void ModuleManager::saveRemap()
{
    std::string file(mapFilePathM);
    rapidjson::Document doc;
    if (rapidjson::parseFile(file.c_str(), doc))
    {
        for (auto it = remapM.begin(); it != remapM.end(); ++it)
        {
            rapidjson::Value* v = rapidjson::getChild(doc, it->first.c_str());
            if (v)
            {
                v->SetString(it->second.c_str(), it->second.size());
            }
            else
            {
                rapidjson::Value _k(rapidjson::kStringType);
                _k.SetString(it->first.c_str(), it->first.size());
                rapidjson::Value _v(rapidjson::kStringType);
                _v.SetString(it->second.c_str(), it->second.size());
                doc.AddMember(_k, _v, doc.GetAllocator());
            }
        }
        rapidjson::saveFile(mapFilePathM.c_str(), doc);
    }
}

/*
** 函数名称: loop()
** 功能描述: 外部调用函数,循环周期检查模组文件
** 参    数: 无
** 返 回 值: bool               有无更新
** 备    注: 函数仅表示串口映射文件是否有更新,但是更新的内容是否应用于工程,需要看工程配置
*/
bool ModuleManager::loop()
{
    //终端重启时，无法快速生成module.json 需要检查三次
    do
    {
        uint64_t curTime = getMilliseconds();
        if (curTime > nextCheckTimeM)
        {
            nextCheckTimeM = curTime + checkTimeM;
            bool ok = check();

            if (ok)
            {
                initCntM = 0;
                return ok;
            }
            else{}

            if (initCntM)
            {
                log_warn("/tmp/module.json has not been changed. It will be checked again in one cycle.");
                --initCntM;
            }
        }
        else{}

        if (initCntM)
        {
            mSleep(2000);
        }
    }while (initCntM);
    return false;
}